// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <algorithm>
#include <iterator>
#include <type_traits>

#include <deneb/entry.h>
#include <deneb/internal/base_iterator.h>
#include <deneb/internal/definitions.h>
#include <deneb/internal/optional.h>
#include <deneb/iterator_tags.h>


namespace deneb {

    // Forward declaration.
    template<typename, typename, typename, typename, typename>
    class TimedCache;
} // namespace deneb

namespace deneb::internal {
    template<typename Cache, typename UnderlyingIterator>
    using BaseForBaseUnorderedIterator =
            BaseIterator<std::forward_iterator_tag,
                    decltype(UnderlyingIterator()->first),
                    decltype(UnderlyingIterator()->second.value),
                    Cache,
                    UnderlyingIterator>;

/// The base class for all const and non-const unordered iterators.
///
/// An unordered iterator is a wrapper around an `unordered_map` iterator with
/// ForwardIterator category. As such, it is (nearly) as fast to access the pair
/// as through the unordered iterator as through the map iterator directly.
/// However, the order of keys is unspecified. For this reason, unordered
/// iterators have the special property that they may be used to construct
/// ordered iterators, after which the order of insertion is respected.
///
/// \tparam Cache The type of the cache instances of the iterator point into.
/// \tparam UnderlyingIterator The underlying iterator class used to implement
///                            the iterator.
    template<typename Cache, typename UnderlyingIterator>
    class BaseUnorderedIterator
            : public BaseForBaseUnorderedIterator<Cache, UnderlyingIterator> {
    protected:
        using super = BaseForBaseUnorderedIterator<Cache, UnderlyingIterator>;
        using PRIVATE_BASE_ITERATOR_MEMBERS;
        // These are the key and value types the BaseIterator extracts
        using Key = typename super::KeyType;
        using Value = typename super::ValueType;

    public:
        using Tag = deneb::deneb_tag::UnorderedIterator;
        using PUBLIC_BASE_ITERATOR_MEMBERS;

        /// Constructor.
        BaseUnorderedIterator() noexcept = default;

        /// \copydoc BaseIterator::BaseIterator(Cache,UnderlyingIterator)
        explicit BaseUnorderedIterator(Cache &cache,
                                       const UnderlyingIterator &iterator) noexcept
                : super(cache, iterator) {
        }

        /// Generalized copy constructor.
        ///
        /// Useful mainly for non-const to const conversion.
        ///
        /// \param other The iterator to copy from.
        template<typename AnyCache, typename AnyUnderlyingIterator>
        BaseUnorderedIterator(
                const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator> &
                other) noexcept
                : super(other) {
        }

        /// Copy constructor.
        BaseUnorderedIterator(const BaseUnorderedIterator &other) noexcept = default;

        /// Move constructor.
        BaseUnorderedIterator(BaseUnorderedIterator &&other) noexcept = default;

        /// Copy assignment operator.
        BaseUnorderedIterator &
        operator=(const BaseUnorderedIterator &other) noexcept = default;

        /// Move assignment operator.
        template<typename AnyCache, typename AnyUnderlyingIterator>
        BaseUnorderedIterator &
        operator=(BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>
                  unordered_iterator) noexcept {
            swap(unordered_iterator);
            return *this;
        }

        /// Destructor.
        virtual ~BaseUnorderedIterator() = default;

        /// Compares this iterator for equality with another unordered iterator.
        ///
        /// \param other Another unordered iterator.
        /// \returns True if both iterators point to the same entry, else false.
        template<typename AnyCache, typename AnyIterator>
        bool
        operator==(const BaseUnorderedIterator<AnyCache, AnyIterator> &other) const
        noexcept {
            return this->_iterator == other._iterator;
        }

        /// Compares this iterator for inequality with another unordered iterator.
        ///
        /// \param other Another unordered iterator.
        /// \returns True if the iterators point to different entries, else false.
        template<typename AnyCache, typename AnyIterator>
        bool
        operator!=(const BaseUnorderedIterator<AnyCache, AnyIterator> &other) const
        noexcept {
            return !(*this == other);
        }

        /// Increments the iterator to the next entry.
        ///
        /// If the iterator already pointed to the end any number of increments
        /// before, behavior is undefined.
        ///
        /// \returns The resulting iterator.
        BaseUnorderedIterator &operator++() {
            ++_iterator;
            _entry.reset();
            return *this;
        }

        /// Increments the iterator and returns a copy of the previous one.
        ///
        /// If the iterator already pointed to the end any number of increments
        /// before, behavior is undefined.
        ///
        /// \returns A copy of the previous iterator.
        BaseUnorderedIterator operator++(int) {
            auto previous = *this;
            ++*this;
            return previous;
        }

        /// \copydoc BaseIterator::operator*
        /// \details If the iterator is invalid, behavior is undefined. No exception
        /// handling is performed.
        Entry &operator*() noexcept override {
            if (!_entry.has_value()) {
                _entry.emplace(_iterator->first, _iterator->second.value);
            }

            return *_entry;
        }

        /// \returns A reference to the entry the iterator points to.
        /// \throws deneb::deneb_error::InvalidIterator if the iterator is the end iterator.
        /// \throws deneb::deneb_error::KeyExpired if the key pointed to by the iterator has
        /// expired.
        Entry &entry() override {
            if (!_entry.has_value()) {
                _entry.emplace(_iterator->first, _iterator->second.value);
            }

            _cache->throw_if_invalid(*this);
            return *_entry;
        }

        /// \returns A reference to the key the iterator points to.
        /// \throws deneb::deneb_error::InvalidIterator if the iterator is the end iterator.
        /// \throws deneb::deneb_error::KeyExpired if the key pointed to by the iterator has
        /// expired.
        const Key &key() override {
            return entry().key();
        }

        /// \returns A reference to the value the iterator points to.
        /// \throws deneb::deneb_error::InvalidIterator if the iterator is the end iterator.
        /// \throws deneb::deneb_error::KeyExpired if the key pointed to by the iterator has
        /// expired.
        Value &value() override {
            return entry().value();
        }

    protected:
        template<typename, typename, typename>
        friend
        class BaseOrderedIterator;

        template<typename, typename, typename, typename, typename>
        friend
        class deneb::TimedCache;
    };

}  // namespace deneb::internal

